package async

import (
	"enotify/pkg/async/errors"
	"sync"
	"sync/atomic"
	"time"
)

type IDGen interface {
	Apply(bizType int32, step int) (id int64, err error)
	MaxId(bizType int32) (id int64, err error)
}
type IDManager struct {
	ids  map[int32]*Id
	mu   sync.RWMutex
	gen  IDGen
	step int
}

//要实现超时申请，超过一定时间就返回超时
func (m *IDManager) Get(bizType int32) (id int64, err error) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	if _, ok := m.ids[bizType]; !ok {
		err = errors.ErrNotExisitBizType
		return
	}
	id = m.ids[bizType].get()
	return
}
func (m *IDManager) Close() () {
	m.mu.Lock()
	for _,rid := range m.ids {
		rid.close()
	}
	defer m.mu.Unlock()
}
func NewIDManager(bizTypes []int32, gen IDGen, step int) (*IDManager, error) {
	ids := make(map[int32]*Id, len(bizTypes))
	for _, v := range bizTypes {
		req, err := newId(gen, v, step)
		if err != nil {
			return nil, err
		}
		ids[v] = req
	}
	var lock sync.RWMutex
	m := &IDManager{
		ids: ids,
		gen: gen,
		mu: lock,
	}
	return m, nil
}

type Id struct {
	gen     IDGen
	start   int64
	end     int64
	ch      chan int64
	bizType int32
	step    int
	stop    chan struct{}
}

//go routine 没有关闭处理,执行一定次数失败之后，要进行异常处理
func newId(gen IDGen, bizType int32, step int) (*Id, error) {
	ch := make(chan int64, 10)
	rid := &Id{
		start:   0,
		end:     0,
		bizType: bizType,
		step:    step,
		ch:      ch,
		gen:     gen,
		stop:    make(chan struct{}, 0),
	}
	id, err := rid.gen.Apply(rid.bizType, rid.step)
	if err != nil {
		return nil, err
	}
	rid.start = id
	rid.end = rid.start + int64(rid.step)
	go func() {
		for {
			select {
			case <-rid.stop:
				return
			default:
				if rid.start < rid.end {
					rid.ch <- rid.start
					rid.start++
					continue
				}
				nextId, err := rid.gen.Apply(rid.bizType, rid.step)
				if err != nil {
					time.Sleep(time.Microsecond * 20)
					continue
				}
				rid.start = nextId
				rid.end = rid.start + int64(rid.step)
			}

		}
	}()
	return rid, nil
}
func (r *Id) get() (id int64) {
	return <-r.ch
}
func (r *Id) set(start, end int64) (err error) {
	if !atomic.CompareAndSwapInt64(&r.start, r.start, start) {
		return errors.ErrReqId
	}
	atomic.StoreInt64(&r.end, end)
	return
}
func (r *Id) close(){
	close(r.stop)
}
